
const Util = require('Util');

const RoadBlock = require('RoadBlock');
const Item = require('Item');

const LevelFactory = require('LevelFactory');
const RoadBlockFactory = require('RoadBlockFactory');


const HpRange = {
    1: [0.01, 0.3],
    2: [0.3, 0.6],
    3: [0.6, 0.9],
    4: [1, 1.5]
};

const HpColor = {
    1: [cc.color(220, 236, 100), cc.color(186, 229, 73), cc.color(142, 231, 104), cc.color(63, 229, 135), cc.color(32, 236, 183)],
    2: [cc.color(32, 236, 183), cc.color(32, 236, 229), cc.color(32, 209, 236), cc.color(32, 192, 236), cc.color(49, 171, 255)],
    3: [cc.color(49, 171, 255), cc.color(89, 128, 255), cc.color(115, 97, 255), cc.color(160, 81, 255), cc.color(204, 59, 255)],
    4: [cc.color(204, 59, 255), cc.color(221, 76, 243), cc.color(242, 84, 215), cc.color(255, 85, 171), cc.color(255, 93, 85)]
};


cc.Class({
    extends: cc.Component,

    properties: {
        bg: cc.Sprite,
        airShip: require('AirShip'),

        scrollSpeed: 300,
        maxScrollSpeed: 1100,
        scrollSpeedStep: 50,

        rushDuration: 5,
        rushSpeed: 1500,
        rushPosition: cc.Vec2
    },

    onLoad() {
        this.viewSize = cc.size(this.node.width, this.node.height);
        this.viewOffset = this.viewSize.height * this.node.parent.anchorY;

        this.isScrolling = false;

        this.scrollDistance = 0;
        this._scrollStep = 1;

        this.isEnded = false;

        this._lvNodes = [];

        this.rushMode = false;
        this._rushTime = 0;

        // Init bg
        this._bgNodes = [this.bg.node];
        this._initBg();

        // Init airShip
        this._initAirShip();


        // Save init state
        this._initData = {
            scrollSpeed: this.scrollSpeed,
            maxScrollSpeed: this.maxScrollSpeed,

            airShipPos: this.airShip.position
        };
    },
    start() {

    },
    update(dt) {
        if (!this.isScrolling) return;

        // Rush
        if (this.rushMode) {
            this._rushTime = Math.min(this._rushTime + dt, this.rushDuration);

            this.airShip.setPowerGroovePercent( (this.rushDuration - this._rushTime) / this.rushDuration );

            if (this._rushTime == this.rushDuration) {
                this.disableRushMode();
                this.clearAllBlocks();
            }
        }

        // this.scrollSpeed = 600;
        let deltaY = dt * this.scrollSpeed;
        this.scrollDistance += deltaY;

        if (this.scrollDistance > this._scrollStep * 10000) {
            this.scrollSpeed = Math.min(this.scrollSpeed + this.scrollSpeedStep, this.maxScrollSpeed);

            this._scrollStep++;
        }

        this.node.y -= deltaY;

        // Scroll bg
        let bgNode1 = this._bgNodes[0];
        if (bgNode1.y + this.node.y < -bgNode1.height) {
            let bgNode = this._bgNodes.shift();
            bgNode1 = this._bgNodes[0];
            bgNode.y = bgNode1.y + bgNode1.height;
            this._bgNodes.push(bgNode);
        }

        // Level
        let lvDone = this.updateLevelView();
        if (lvDone) {
            this.addLevelNode();
        }
        let prevLvNode = this._lvNodes[1];
        if (prevLvNode) {
            let offset = this._getLevelNodeOffset(prevLvNode);
            // cc.log('--PREV-LV:',prevLvNode.height,offset);
            if (prevLvNode.height <= offset) {
                this._lvNodes.splice(1, 1);
                prevLvNode.destroy();
            }
        }
    },

    _initBg() {
        this.bg.node.zIndex = -1;

        let bgNode = new cc.Node();
        let bgSp = bgNode.addComponent(cc.Sprite);
        bgSp.spriteFrame = this.bg.spriteFrame;
        bgSp.sizeMode = this.bg.sizeMode;
        bgNode.parent = this.bg.node.parent;
        bgNode.zIndex = this.bg.node.zIndex;
        bgNode.setContentSize( this.bg.node.getContentSize() );
        bgNode.y = bgNode.height;

        this._bgNodes.push(bgNode);
    },
    _initAirShip() {
        let width = this.viewSize.width / 2;
        this.airShip.setBoundary([-width, width]);

        this._airShipChecker = (other, self) => {
            // cc.log('--AIRSHIP-CE-B:',self.world.aabb,other.world.aabb);
            if (self.world.aabb.y <= other.world.aabb.y) {
                return true;
            }

            // cc.log('--BR:',this.boundaryRange, (other.world.aabb.width+self.world.aabb.width)/2, cc.rectGetMidX(self.world.aabb)-cc.rectGetMidX(other.world.aabb));
            if (other.world.aabb.x < self.world.aabb.x) {
                this.airShip.x += other.world.aabb.width - (self.world.aabb.x - other.world.aabb.x);
            } else {
                this.airShip.x -= other.world.aabb.width - (other.world.aabb.x - self.world.aabb.x);
            }
            return false;
        };
        this.airShip.setCollisionChecker(this._airShipChecker);

        this.airShip.on('hit-block', ({detail: block}) => {
            if (this.rushMode) {
                block.die();
                return;
            }

            if (this.isEnded) return;
            this.isEnded = true;

            game.battleManager.emit('end');
        });
        this.airShip.on('hit-item', ({detail: item}) => {
            switch (item.type) {
                case Item.Type.BULLET_DAMAGE:
                    this.airShip.bulletDamage += 2;
                    break;
                case Item.Type.SHOOT_SPEED:
                    this.airShip.shootSpeed *= 1 - 0.1;
                    break;
                case Item.Type.BULLET_NUM:
                    this.airShip.addBulletLine();
                    break;
            }

            this.airShip.useItem(item);

            item.removeSelf();

            this.node.emit('use-item');
        });
        this.airShip.on('hit-shield', ({detail: block}) => {
            block.die();

            this.airShip.setShieldActive(false);
        });
        this.airShip.on('fill-power', () => {
            this.enableRushMode();
        });
        this.airShip.on('shoot-bullet', () => {
            this.node.emit('shoot-bullet');
        });
    },

    scroll() {
        this.isScrolling = true;

        this.airShip.shoot();
    },
    stopScroll() {
        this.isScrolling = false;

        this.airShip.stopShoot();
    },

    init() {
        this.addLevelNode();
    },
    restart() {
        this.scrollDistance = 0;
        this._scrollStep = 1;

        this.scrollSpeed = this._initData.scrollSpeed;

        this.airShip.reset();
        this.airShip.position = this._initData.airShipPos;

        this.retry();
    },
    retry() {
        this.isEnded = false;

        this.node.y = 0;

        let distance = this._bgNodes[0].y;
        for (let bgNode of this._bgNodes) {
            bgNode.y -= distance;
        }

        this._lvNodes.forEach(lvNode => lvNode.destroy());
        this._lvNodes = [];

        this.addLevelNode();
    },

    addLevelNode(callback) {
        let lvId = Math.min(this._scrollStep, LevelFactory.lastLevelId);

        LevelFactory.generateLevel(lvId, (level) => {
            level.node.on('update-size', () => {
                // Util.showBorder(level.node, cc.color(Util.random(0, 255), Util.random(0, 255), Util.random(0, 255)), level.node.parent);
            });
            level.node.on('hit-bullet', ({detail: {block, bullet}}) => {

            });
            level.node.on('create-obj', ({detail: cellObj}) => {
                // cc.log('--L-C-OBJ:',cellObj.constructor.name);
                if (cellObj instanceof RoadBlock) {
                    let block = cellObj;
                    block.colors = HpColor[block.hpType];
                    block.setHp( this.calcBlockHp(block) );

                    block.on('hit-bullet', ({detail: bullet}) => {
                        bullet.removeSelf();

                        let prevHp = block.hp;
                        block.attacked(bullet.damage);
                        // cc.log('--BULLET:',bullet.__instanceId,bullet.damage, block.hp);
                        this.node.emit('score', prevHp - block.hp);
                    });
                    block.on('die', () => {
                        let blockEffect = RoadBlockFactory.createBlockEffect();
                        blockEffect.color = HpColor[block.hpType][0];
                        blockEffect.setBlockType(block.type);

                        let parentNode = this.node.parent;
                        blockEffect.node.parent = parentNode;
                        blockEffect.node.position = parentNode.convertToNodeSpaceAR( block.node.getPositionToWorld() );
                        // cc.log('==B-DIE:', block, blockEffect.node.position);
                        // Util.showBorder(cc.rect(...Object.values(parentNode.convertToWorldSpaceAR(blockEffect.node.position)), 10, 10), cc.Color.WHITE);

                        let particleSystem = blockEffect.getParticleSystem();
                        let duration = particleSystem.duration + particleSystem.life + particleSystem.lifeVar;
                        blockEffect.scheduleOnce(() => {
                            blockEffect.removeSelf();
                        }, duration);

                        this.node.emit('destroy-block');
                    });
                }
            });

            level.node.parent = this.node;

            level.node.x = -level.node.width / 2;
            level.node.y = 0;


            let prevNode = this._lvNodes[this._lvNodes.length - 1];
            cc.log('==L-C:',level.node.name,prevNode && prevNode.height);
            if (prevNode)
                level.node.y = prevNode.y + prevNode.height;

            this._lvNodes.unshift(level.node);

            this.updateLevelView();

            callback && callback(level.node);

        });
    },

    updateLevelView() {
        let lvNode = this._lvNodes[0];

        let offset = this._getLevelNodeOffset(lvNode);
        let viewHeight = this.viewSize.height + Math.max(-this.viewSize.height, Math.min(offset, 0));

        let level = lvNode.getComponent('Level');
        return level.generateView(viewHeight, Math.max(0, offset));
    },
    _getLevelNodeOffset(lvNode) {
        return -(this.node.y + this.viewOffset) - lvNode.y;
    },

    calcBlockHp(block) {
        let airShip = this.airShip;

        let distance = block.y - block.node.parent.convertToNodeSpaceAR( airShip.node.getPositionToWorld() ).y;

        let maxDamage = airShip.bulletDamage * airShip.bulletLines.length * (distance / this.scrollSpeed) / airShip.shootSpeed;

        let hpRange = HpRange[block.hpType];
        // cc.log('--CALC-HP:', maxDamage,hpRange, airShip.bulletDamage,airShip.bulletLines.length,airShip.shootSpeed, distance,this.scrollSpeed);
        return Math.ceil( Util.random(hpRange[0], hpRange[1], 2) * maxDamage );
    },

    calcScrollSpeed() {
        return Math.min(this._initData.scrollSpeed + (this._scrollStep - 1) * this.scrollSpeedStep, this._initData.maxScrollSpeed);
    },

    enableRushMode() {
        this.airShip.position = this.rushPosition;
        this.airShip.setCollisionChecker(() => true);

        this.scrollSpeed = this.rushSpeed;
        this.maxScrollSpeed = this.rushSpeed;

        this.rushMode = true;
        this._rushTime = 0;
    },
    disableRushMode() {
        this.airShip.y = this._initData.airShipPos.y;
        this.airShip.setCollisionChecker(this._airShipChecker);

        this.scrollSpeed = this.calcScrollSpeed();
        this.maxScrollSpeed = this._initData.maxScrollSpeed;

        this.rushMode = false;
    },

    clearAllBlocks() {
        for (let lvNode of this._lvNodes) {
            let level = lvNode.getComponent('Level');
            level.clearBlocks();
        }
    }
});